/*
 ------------------------------------------------------------------------------
 Copyright (C) 2009-2011 Eternal Games.

 This file is part of the Eternal Tech source code.

 The Eternal Tech source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Eternal Tech source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Eternal Tech source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// cl_effects.cpp - ...
//

// TODO:
// - Rewrite particle functions to support script parameters
// - For a tracer particle, use something like the bubbletrail particle?


#include "cl_local.h"
#include "client.h"


/*
 ==============================================================================

    PARTICLE EFFECTS MANAGEMENT

 ==============================================================================
*/

static particle_t			cl_particleList[MAX_PARTICLES];
static particle_t			*cl_activeParticles;
static particle_t			*cl_freeParticles;


/*
 ==================
 CL_AllocParticle
 ==================
*/
static particle_t *CL_AllocParticle ()
{
	particle_t	*particle;

	if (!cl_freeParticles)
	{
		Com_DevPrintf("CL_AllocParticle: MAX_PARTICLES hit\n");
		return NULL;
	}

	// Get a free slot
	particle = cl_freeParticles;
	cl_freeParticles = particle->next;

	// Link
	particle->next = cl_activeParticles;
	cl_activeParticles = particle;

	return particle;
}

/*
 ==================
 CL_FreeParticle
 ==================
*/
static void CL_FreeParticle (particle_t *particle)
{
	// Free the slot
	particle->next = cl_freeParticles;
	cl_freeParticles = particle;
}

/*
 ==================
 CL_SetParticleFlags

 Returns true if the particle was freed
 ==================
*/
static bool CL_SetParticleFlags (particle_t *particle, vec3_t origin, vec3_t oldOrigin, float gravity, float radius, int length, vec3_t color, float alpha)
{
	trace_t trace;
	vec3_t	mins, maxs;
	vec3_t  vel;
	float	dot;
	int		contents;
	int		time;

	if (particle->flags == PF_NONE)
		return false;

	// Underwater particle
	if (particle->flags & PF_UNDERWATER)
	{
		VectorSet(oldOrigin, origin[0], origin[1], origin[2] + radius);

		// Not underwater
		if (!(CL_PointContents(oldOrigin, -1) & MASK_WATER))
		{
			CL_FreeParticle(particle);
			return true;
		}
	}

	// Water friction affected particle
	if (particle->flags & PF_FRICTION)
	{
		contents = CL_PointContents(origin, -1);
		if (contents & MASK_WATER)
		{
			// Add friction
			if (contents & CONTENTS_WATER)
			{
				VectorScale(particle->vel, 0.25f, particle->vel);
				VectorScale(particle->accel, 0.25f, particle->accel);
			}
			if (contents & CONTENTS_SLIME)
			{
				VectorScale(particle->vel, 0.20f, particle->vel);
				VectorScale(particle->accel, 0.20f, particle->accel);
			}
			if (contents & CONTENTS_LAVA)
			{
				VectorScale(particle->vel, 0.10f, particle->vel);
				VectorScale(particle->accel, 0.10f, particle->accel);
			}

			length = 1;

			// Don't add friction again
			particle->flags &= ~PF_FRICTION;

			// Reset
			particle->time = cl.time;
			VectorCopy(origin, particle->org);
			VectorCopy(color, particle->color);
			particle->alpha = alpha;
			particle->radius = radius;

			// Don't stretch
			particle->flags &= ~PF_STRETCH;

			particle->length = length;
			particle->lengthVel = 0;
		}
	}

	// Bouncy particle
	if (particle->flags & PF_BOUNCE)
	{
		// Compute the bounding box
		VectorSet(mins, -radius, -radius, -radius);
		VectorSet(maxs, radius, radius, radius);

		// FIXME: This fails if the bounding box is not on top of the surface and it will fall through

		trace = CL_Trace(particle->lastOrg, mins, maxs, origin, cl.playernum+1, MASK_SOLID, true, NULL);
		if (trace.fraction != 0.0f && trace.fraction != 1.0f)
		{
			// Reflect velocity
			time = cl.time - SEC2MS(cls2.frametime + cls2.frametime * trace.fraction);
			time = MS2SEC(time - particle->time);

			VectorSet(vel, particle->vel[0], particle->vel[1], particle->vel[2] + particle->accel[2] * gravity * time);
			VectorReflect(vel, trace.plane.normal, particle->vel);
			VectorScale(particle->vel, particle->bounceFactor, particle->vel);

			// Check for stop or slide along the plane
			if (trace.plane.normal[2] > 0 && particle->vel[2] < 1)
			{
				if (trace.plane.normal[2] == 1)
				{
					VectorClear(particle->vel);
					VectorClear(particle->accel);

					particle->flags &= ~PF_BOUNCE;
				}
				else
				{
					// FIXME: check for new plane or free fall
					dot = DotProduct(particle->vel, trace.plane.normal);
					VectorMA(particle->vel, -dot, trace.plane.normal, particle->vel);

					dot = DotProduct(particle->accel, trace.plane.normal);
					VectorMA(particle->accel, -dot, trace.plane.normal, particle->accel);
				}
			}

			VectorCopy(trace.endpos, origin);
			length = 1;

			// Reset
			particle->time = cl.time;
			VectorCopy(origin, particle->org);
			VectorCopy(color, particle->color);
			particle->alpha = alpha;
			particle->radius = radius;

			// Don't stretch
			particle->flags &= ~PF_STRETCH;

			particle->length = length;
			particle->lengthVel = 0;
		}
	}

	// Instant particle
	if (particle->flags & PF_INSTANT)
	{
		particle->alpha = 0;
		particle->fadeAlpha = 0;
	}

	// Save current origin if needed
	if (particle->flags & (PF_BOUNCE | PF_STRETCH))
	{
		VectorCopy(particle->lastOrg, oldOrigin);
		VectorCopy(origin, particle->lastOrg);
	}

	return false;
}

/*
 ==================
 CL_BuildParticlePoly

 TODO: Render based on particleType_t
 ==================
*/
static void CL_BuildParticlePoly (particle_t *particle, vec3_t origin, vec3_t oldOrigin, float gravity, float radius, int length, vec3_t color, float alpha)
{
	renderParticle_t	 poly;
	renderPolyVertex_t   *vertices;
	vec3_t				 axis[3];
	color_t				 modulate;
	float				 rad, s, c;
	int					 i;

	// Clamp color and alpha and convert to byte
	modulate[0] = 255 * Clamp(color[0], 0.0f, 1.0f);
	modulate[1] = 255 * Clamp(color[1], 0.0f, 1.0f);
	modulate[2] = 255 * Clamp(color[2], 0.0f, 1.0f);
	modulate[3] = 255 * Clamp(alpha, 0.0f, 1.0f);

	// Build the poly
	poly.material = particle->material;
	poly.numVertices = 4;
	poly.vertices = particle->vertices;

	// Build the vertices
	vertices = particle->vertices;

	if (length != 1.0f)
	{
		// Find orientation vectors
		VectorSubtract(cl.renderView.viewOrigin, origin, axis[0]);
		VectorSubtract(oldOrigin, origin, axis[1]);
		CrossProduct(axis[0], axis[1], axis[2]);

		VectorNormalizeFast(axis[1]);
		VectorNormalizeFast(axis[2]);

		// Find normal
		CrossProduct(axis[1], axis[2], axis[0]);
		VectorNormalizeFast(axis[0]);

		VectorMA(origin, -length, axis[1], oldOrigin);
		VectorScale(axis[2], radius, axis[2]);

		// Set up vertices
		vertices[0].xyz[0] = oldOrigin[0] + axis[2][0];
		vertices[0].xyz[1] = oldOrigin[1] + axis[2][1];
		vertices[0].xyz[2] = oldOrigin[2] + axis[2][2];
		vertices[1].xyz[0] = origin[0] + axis[2][0];
		vertices[1].xyz[1] = origin[1] + axis[2][1];
		vertices[1].xyz[2] = origin[2] + axis[2][2];
		vertices[2].xyz[0] = origin[0] - axis[2][0];
		vertices[2].xyz[1] = origin[1] - axis[2][1];
		vertices[2].xyz[2] = origin[2] - axis[2][2];
		vertices[3].xyz[0] = oldOrigin[0] - axis[2][0];
		vertices[3].xyz[1] = oldOrigin[1] - axis[2][1];
		vertices[3].xyz[2] = oldOrigin[2] - axis[2][2];
	}
	else
	{
		// Calculate axes
		if (particle->rotation)
		{
			rad = DEG2RAD(particle->rotation);
			s = sin(rad);
			c = cos(rad);

			VectorNegate(cl.renderView.viewAxis[0], axis[0]);

			VectorScale(cl.renderView.viewAxis[1], c * radius, axis[1]);
			VectorMA(axis[1], -s * radius, cl.renderView.viewAxis[2], axis[1]);

			VectorScale(cl.renderView.viewAxis[2], c * radius, axis[2]);
			VectorMA(axis[2], s * radius, cl.renderView.viewAxis[1], axis[2]);
		}
		else
		{
			VectorNegate(cl.renderView.viewAxis[0], axis[0]);
			VectorScale(cl.renderView.viewAxis[1], radius, axis[1]);
			VectorScale(cl.renderView.viewAxis[2], radius, axis[2]);
		}

		// Set up vertices
		vertices[0].xyz[0] = origin[0] + axis[1][0] + axis[2][0];
		vertices[0].xyz[1] = origin[1] + axis[1][1] + axis[2][1];
		vertices[0].xyz[2] = origin[2] + axis[1][2] + axis[2][2];
		vertices[1].xyz[0] = origin[0] - axis[1][0] + axis[2][0];
		vertices[1].xyz[1] = origin[1] - axis[1][1] + axis[2][1];
		vertices[1].xyz[2] = origin[2] - axis[1][2] + axis[2][2];
		vertices[2].xyz[0] = origin[0] - axis[1][0] - axis[2][0];
		vertices[2].xyz[1] = origin[1] - axis[1][1] - axis[2][1];
		vertices[2].xyz[2] = origin[2] - axis[1][2] - axis[2][2];
		vertices[3].xyz[0] = origin[0] + axis[1][0] - axis[2][0];
		vertices[3].xyz[1] = origin[1] + axis[1][1] - axis[2][1];
		vertices[3].xyz[2] = origin[2] + axis[1][2] - axis[2][2];
	}

	vertices[0].st[0] = 0.0f;
	vertices[0].st[1] = 0.0f;
	vertices[1].st[0] = 1.0f;
	vertices[1].st[1] = 0.0f;
	vertices[2].st[0] = 1.0f;
	vertices[2].st[1] = 1.0f;
	vertices[3].st[0] = 0.0f;
	vertices[3].st[1] = 1.0f;

	for (i = 0; i < 4; i++)
	{
		vertices->normal[0] = axis[0][0];
		vertices->normal[1] = axis[0][1];
		vertices->normal[2] = axis[0][2];
		vertices->color[0] = modulate[0];
		vertices->color[1] = modulate[1];
		vertices->color[2] = modulate[2];
		vertices->color[3] = modulate[3];

		vertices++;
	}

	// Send the particle geometry to the renderer
	R_AddParticleToScene(&poly);
}

/*
 ==================
 CL_AddParticles
 ==================
*/
void CL_AddParticles ()
{
	particle_t   *particle, *next;
	particle_t	 *active = NULL, *tail = NULL;
	vec3_t		 color, origin, oldOrigin;
	vec3_t		 vec;
	float		 time, fadeTime;
	float		 alpha, radius, length;
	float	     gravity;

	gravity = cl.playerState->pmove.gravity / 800.0f;

	for (particle = cl_activeParticles; particle; particle = next)
	{
		// Grab next now, so if the particle is freed we still have it
		next = particle->next;

		// Compute values
		time = MS2SEC(cl.time - particle->time);
		fadeTime = time * time;

		alpha = particle->alpha + particle->fadeAlpha * time;
		radius = particle->radius + particle->fadeRadius * time;
		length = particle->length + particle->lengthVel * time;

		// Faded out so free the particle
		if (alpha <= 0 || radius <= 0 || length <= 0)
		{
			CL_FreeParticle(particle);
			continue;
		}

		color[0] = particle->color[0] + particle->fadeColor[0] * time;
		color[1] = particle->color[1] + particle->fadeColor[1] * time;
		color[2] = particle->color[2] + particle->fadeColor[2] * time;

		origin[0] = particle->org[0] + particle->vel[0] * time + particle->accel[0] * fadeTime;
		origin[1] = particle->org[1] + particle->vel[1] * time + particle->accel[1] * fadeTime;
		origin[2] = particle->org[2] + particle->vel[2] * time + particle->accel[2] * fadeTime * gravity;

		// Set flags if any
		if (CL_SetParticleFlags(particle, origin, oldOrigin, gravity, radius, length, color, alpha))
			continue;

		// Add it to the last index of the list
		particle->next = NULL;
		if (!tail)
			active = tail = particle;
		else
		{
			tail->next = particle;
			tail = particle;
		}

		// Cull from the view axis
		VectorSubtract(origin, cl.renderView.viewOrigin, vec);
		if (DotProduct(vec, cl.renderView.viewAxis[0]) < 0)
			continue;

		// Cull distance
		if (Distance(origin, cl.renderView.viewOrigin) > 1024.0f)
			continue;

		// Build particle polygon geometry
		CL_BuildParticlePoly(particle, origin, oldOrigin, gravity, radius, length, color, alpha);
	}

	cl_activeParticles = active;
}

/*
 ==================
 CL_ClearParticles
 ==================
*/
void CL_ClearParticles ()
{
	int		i;

	cl_activeParticles = NULL;
	cl_freeParticles = cl_particleList;

	for (i = 0; i < MAX_PARTICLES; i++)
		cl_particleList[i].next = &cl_particleList[i+1];

	cl_particleList[MAX_PARTICLES-1].next = NULL;
}

/*
 ==================
 CL_BlasterTrail
 ==================
*/
void CL_BlasterTrail (const vec3_t start, const vec3_t end, particleSource_t *particle)
{
	particle_t   *p;
	vec3_t		 move, vec;
	float		 length, dist;

	VectorCopy(start, move);
	VectorSubtract(end, start, vec);
	length = VectorNormalize(vec);

	dist = 4.5f;
	VectorScale(vec, dist, vec);

	while (length > 0){
		length -= dist;

		p = CL_AllocParticle();
		if (!p)
			return;

		p->material = particle->stages->material;
		p->time = cl.time;
		p->flags = PF_NONE;

		p->org[0] = move[0] + crand();
		p->org[1] = move[1] + crand();
		p->org[2] = move[2] + crand();
		p->vel[0] = crand() * 5;
		p->vel[1] = crand() * 5;
		p->vel[2] = crand() * 5;
		p->accel[0] = 0;
		p->accel[1] = 0;
		p->accel[2] = 0;
		p->color[0] = particle->stages->color[0];
		p->color[1] = particle->stages->color[1];
		p->color[2] = particle->stages->color[2];
		p->fadeColor[0] = particle->stages->fadeColor[0];
		p->fadeColor[1] = particle->stages->fadeColor[1];
		p->fadeColor[2] = particle->stages->fadeColor[2];
		p->alpha = 1.0f;
		p->fadeAlpha = -1.0f / (0.3f + frand() * 0.2f);
		p->radius = 2.4f + (1.2f * crand());
		p->fadeRadius = -2.4f + (1.2f * crand());
		p->length = 1;
		p->lengthVel = 0;
		p->rotation = 0;

		VectorAdd(move, vec, move);
	}
}

/*
 ==================
 CL_GrenadeTrail
 ==================
*/
void CL_GrenadeTrail (const vec3_t start, const vec3_t end, particleSource_t *particle)
{
	particle_t   *p;
	vec3_t		 move, vec;
	float		 length, dist;

	// If underwater, do a bubble trail
	if (CL_PointContents(end, -1) & MASK_WATER)
	{
		if (CL_PointContents(start, -1) & MASK_WATER)
			CL_BubbleTrail(start, end, 16, 1, cl.media.bubbleTrailParticle);

		return;
	}

	VectorCopy(start, move);
	VectorSubtract(end, start, vec);
	length = VectorNormalize(vec);

	dist = 20;
	VectorScale(vec, dist, vec);

	while (length > 0){
		length -= dist;

		p = CL_AllocParticle();
		if (!p)
			return;

		p->material = particle->stages->material;
		p->time = cl.time;
		p->flags = PF_NONE;

		p->org[0] = move[0] + crand();
		p->org[1] = move[1] + crand();
		p->org[2] = move[2] + crand();
		p->vel[0] = crand() * 5;
		p->vel[1] = crand() * 5;
		p->vel[2] = crand() * 5 + (25 + crand() * 5);
		p->accel[0] = 0;
		p->accel[1] = 0;
		p->accel[2] = 0;
		p->color[0] = particle->stages->color[0];
		p->color[1] = particle->stages->color[1];
		p->color[2] = particle->stages->color[2];
		p->fadeColor[0] = particle->stages->fadeColor[0];
		p->fadeColor[1] = particle->stages->fadeColor[1];
		p->fadeColor[2] = particle->stages->fadeColor[2];
		p->alpha = 0.5;
		p->fadeAlpha = -(0.4f + frand() * 0.2f);
		p->radius = 3 + (1.5f * crand());
		p->fadeRadius = 3 + (1.5f * crand());
		p->length = 1;
		p->lengthVel = 0;
		p->rotation = rand() % 360;

		VectorAdd(move, vec, move);
	}
}

/*
 ==================
 CL_RocketTrail

 TODO: remove particle2 later
 ==================
*/
void CL_RocketTrail (const vec3_t start, const vec3_t end, particleSource_t *particle, particleSource_t *particle2)
{
	particle_t   *p;
	vec3_t		 move, vec;
	float		 length, dist;

	// If underwater, do a bubble trail
	if (CL_PointContents(end, -1) & MASK_WATER)
	{
		if (CL_PointContents(start, -1) & MASK_WATER)
			CL_BubbleTrail(start, end, 8, 3, cl.media.bubbleTrailParticle);

		return;
	}

	// Flames
	VectorCopy(start, move);
	VectorSubtract(end, start, vec);
	length = VectorNormalize(vec);

	dist = 1;
	VectorScale(vec, dist, vec);

	while (length > 0){
		length -= dist;

		p = CL_AllocParticle();
		if (!p)
			return;

		p->material = particle->stages->material;
		p->time = cl.time;
		p->flags = PF_NONE;

		p->org[0] = move[0] + crand();
		p->org[1] = move[1] + crand();
		p->org[2] = move[2] + crand();
		p->vel[0] = crand() * 5;
		p->vel[1] = crand() * 5;
		p->vel[2] = crand() * 5 + (25 + crand() * 5);
		p->accel[0] = 0;
		p->accel[1] = 0;
		p->accel[2] = 0;
		p->color[0] = particle->stages->color[0];
		p->color[1] = particle->stages->color[1];
		p->color[2] = particle->stages->color[2];
		p->fadeColor[0] = particle->stages->fadeColor[0];
		p->fadeColor[1] = particle->stages->fadeColor[1];
		p->fadeColor[2] = particle->stages->fadeColor[2];
		p->alpha = 1.0f;
		p->fadeAlpha = -2.0f / (0.2f + frand() * 0.1f);
		p->radius = 3 + (1.5f * crand());
		p->fadeRadius = -6 + (3 * crand());
		p->length = 1;
		p->lengthVel = 0;
		p->rotation = rand() % 360;

		VectorAdd(move, vec, move);
	}
	
	// Smoke
	VectorCopy(start, move);
	VectorSubtract(end, start, vec);
	length = VectorNormalize(vec);

	dist = 10;
	VectorScale(vec, dist, vec);

	while (length > 0){
		length -= dist;

		p = CL_AllocParticle();
		if (!p)
			return;

		p->material = particle2->stages->material;
		p->time = cl.time;
		p->flags = PF_NONE;

		p->org[0] = move[0] + crand();
		p->org[1] = move[1] + crand();
		p->org[2] = move[2] + crand();
		p->vel[0] = crand() * 5;
		p->vel[1] = crand() * 5;
		p->vel[2] = crand() * 5 + (25 + crand() * 5);
		p->accel[0] = 0;
		p->accel[1] = 0;
		p->accel[2] = 0;
		p->color[0] = particle2->stages->color[0];
		p->color[1] = particle2->stages->color[1];
		p->color[2] = particle2->stages->color[2];
		p->fadeColor[0] = particle->stages->fadeColor[0];
		p->fadeColor[1] = particle->stages->fadeColor[1];
		p->fadeColor[2] = particle->stages->fadeColor[2];
		p->alpha = 0.5f;
		p->fadeAlpha = -(0.2f + frand() * 0.1f);
		p->radius = 3 + (1.5f * crand());
		p->fadeRadius = 15 + (7.5f * crand());
		p->length = 1;
		p->lengthVel = 0;
		p->rotation = rand() % 360;

		VectorAdd(move, vec, move);
	}
}

/*
 ==================
 CL_RailTrail

 TODO: remove particle2 later on
 ==================
*/
void CL_RailTrail (const vec3_t start, const vec3_t end, particleSource_t *particle, particleSource_t *particle2)
{
	particle_t   *p;
	vec3_t		 move, vec;
	float		 length, dist;
	int			 i;
	vec3_t		 right, up, dir;
	float		 d, s, c;

	// Core
	VectorCopy(start, move);
	VectorSubtract(end, start, vec);
	length = VectorNormalize(vec);

	dist = 2;
	VectorScale(vec, dist, vec);

	while (length > 0){
		length -= dist;

		p = CL_AllocParticle();
		if (!p)
			return;

		p->material = particle->stages->material;
		p->time = cl.time;
		p->flags = PF_NONE;

		p->org[0] = move[0] + crand();
		p->org[1] = move[1] + crand();
		p->org[2] = move[2] + crand();
		p->vel[0] = crand() * 1.5f;
		p->vel[1] = crand() * 1.5f;
		p->vel[2] = crand() * 1.5f;
		p->accel[0] = 0;
		p->accel[1] = 0;
		p->accel[2] = 0;
		p->color[0] = particle->stages->color[0];
		p->color[1] = particle->stages->color[1];
		p->color[2] = particle->stages->color[2];
		p->fadeColor[0] = particle->stages->fadeColor[0];
		p->fadeColor[1] = particle->stages->fadeColor[1];
		p->fadeColor[2] = particle->stages->fadeColor[2];
		p->alpha = 0.5f;
		p->fadeAlpha = -0.5f / (1.0f + frand() * 0.2f);
		p->radius = 1.5f + (0.5f * crand());
		p->fadeRadius = 3 + (1.5f * crand());
		p->length = 1;
		p->lengthVel = 0;
		p->rotation = rand() % 360;

		VectorAdd(move, vec, move);
	}

	// Spiral
	VectorCopy(start, move);
	VectorSubtract(end, start, vec);
	length = VectorNormalize(vec);

	MakeNormalVectors(vec, right, up);

	for (i = 0; i < length; i++)
	{
		p = CL_AllocParticle();
		if (!p)
			return;

		d = i * 0.1f;
		s = sin(d);
		c = cos(d);

		dir[0] = right[0]*c + up[0]*s;
		dir[1] = right[1]*c + up[1]*s;
		dir[2] = right[2]*c + up[2]*s;

		p->material = particle2->stages->material;
		p->time = cl.time;
		p->flags = PF_NONE;

		p->org[0] = move[0] + dir[0] * 2.5f;
		p->org[1] = move[1] + dir[1] * 2.5f;
		p->org[2] = move[2] + dir[2] * 2.5f;
		p->vel[0] = dir[0] * 5;
		p->vel[1] = dir[1] * 5;
		p->vel[2] = dir[2] * 5;
		p->accel[0] = 0;
		p->accel[1] = 0;
		p->accel[2] = 0;
		p->color[0] = particle2->stages->color[0];
		p->color[1] = particle2->stages->color[1];
		p->color[2] = particle2->stages->color[2];
		p->fadeColor[0] = particle->stages->fadeColor[0];
		p->fadeColor[1] = particle->stages->fadeColor[1];
		p->fadeColor[2] = particle->stages->fadeColor[2];
		p->alpha = 1.0f;
		p->fadeAlpha = -1.0f;
		p->radius = 2.5f;
		p->fadeRadius = 0;
		p->length = 1;
		p->lengthVel = 0;
		p->rotation = 0;

		VectorAdd(move, vec, move);
	}
}

/*
 ==================
 CL_BubbleTrail
 ==================
*/
void CL_BubbleTrail (const vec3_t start, const vec3_t end, float dist, float radius, particleSource_t *particle)
{
	particle_t   *p;
	vec3_t		 move, vec;
	float		 length;

	VectorCopy(start, move);
	VectorSubtract(end, start, vec);
	length = VectorNormalize(vec);

	VectorScale(vec, dist, vec);

	while (length > 0){
		length -= dist;

		p = CL_AllocParticle();
		if (!p)
			return;

		p->material = particle->stages->material;
		p->time = cl.time;
		p->flags = PF_UNDERWATER;

		p->org[0] = move[0] + crand();
		p->org[1] = move[1] + crand();
		p->org[2] = move[2] + crand();
		p->vel[0] = crand() * 5;
		p->vel[1] = crand() * 5;
		p->vel[2] = crand() * 5 + (25 + crand() * 5);
		p->accel[0] = 0;
		p->accel[1] = 0;
		p->accel[2] = 0;
		p->color[0] = particle->stages->color[0];
		p->color[1] = particle->stages->color[1];
		p->color[2] = particle->stages->color[2];
		p->fadeColor[0] = particle->stages->fadeColor[0];
		p->fadeColor[1] = particle->stages->fadeColor[1];
		p->fadeColor[2] = particle->stages->fadeColor[2];
		p->alpha = 1.0f;
		p->fadeAlpha = -(0.4f + frand() * 0.2f);
		p->radius = radius + ((radius * 0.5f) * crand());
		p->fadeRadius = 0;
		p->length = 1;
		p->lengthVel = 0;
		p->rotation = 0;

		VectorAdd(move, vec, move);
	}
}

// TODO: Make this effect kinda like TRON (movie) car wall thing?
void CL_FlagTrail (const vec3_t start, const vec3_t end, particleSource_t *particle)
{
}

void CL_TracerTrail (const vec3_t start, const vec3_t end, particleSource_t *particle)
{
}

/*
 ==================
 CL_BlasterParticles

 TODO: remove particle2 later
 ==================
*/
void CL_BlasterParticles (const vec3_t org, const vec3_t dir, particleSource_t *particle, particleSource_t *particle2)
{
	particle_t   *p;
	int			 i;

	// Sparks
	for (i = 0; i < 40; i++)
	{
		p = CL_AllocParticle();
		if (!p)
			return;

		p->material = particle->stages->material;
		p->time = cl.time;
		p->flags = PF_BOUNCE | PF_FRICTION;

		p->org[0] = org[0] + dir[0] * 3 + crand();
		p->org[1] = org[1] + dir[1] * 3 + crand();
		p->org[2] = org[2] + dir[2] * 3 + crand();
		p->vel[0] = dir[0] * 25 + crand() * 20;
		p->vel[1] = dir[1] * 25 + crand() * 20;
		p->vel[2] = dir[2] * 50 + crand() * 20;
		p->accel[0] = 0;
		p->accel[1] = 0;
		p->accel[2] = -120 + (40 * crand());
		p->color[0] = particle->stages->color[0];
		p->color[1] = particle->stages->color[1];
		p->color[2] = particle->stages->color[2];
		p->fadeColor[0] = particle->stages->fadeColor[0];
		p->fadeColor[1] = particle->stages->fadeColor[1];
		p->fadeColor[2] = particle->stages->fadeColor[2];
		p->alpha = 1.0f;
		p->fadeAlpha = -0.25f / (0.3f + frand() * 0.2f);
		p->radius = 2.4f + (1.2f * crand());
		p->fadeRadius = -1.2f + (0.6f * crand());
		p->length = 1;
		p->lengthVel = 0;
		p->rotation = 0;
		p->bounceFactor = 0.7f;

		VectorCopy(p->org, p->lastOrg);
	}

	// If underwater, skip
	if (CL_PointContents(org, -1) & MASK_WATER)
		return;

	// Steam
	for (i = 0; i < 3; i++)
	{
		p = CL_AllocParticle();
		if (!p)
			return;

		p->material = particle2->stages->material;
		p->time = cl.time;
		p->flags = PF_NONE;

		p->org[0] = org[0] + dir[0] * 5 + crand();
		p->org[1] = org[1] + dir[1] * 5 + crand();
		p->org[2] = org[2] + dir[2] * 5 + crand();
		p->vel[0] = crand() * 2.5f;
		p->vel[1] = crand() * 2.5f;
		p->vel[2] = crand() * 2.5f + (25 + crand() * 5);
		p->accel[0] = 0;
		p->accel[1] = 0;
		p->accel[2] = 0;
		p->color[0] = particle2->stages->color[0];
		p->color[1] = particle2->stages->color[1];
		p->color[2] = particle2->stages->color[2];
		p->fadeColor[0] = particle2->stages->fadeColor[0];
		p->fadeColor[1] = particle2->stages->fadeColor[1];
		p->fadeColor[2] = particle2->stages->fadeColor[2];
		p->alpha = 0.5f;
		p->fadeAlpha = -(0.4f + frand() * 0.2f);
		p->radius = 3 + (1.5f * crand());
		p->fadeRadius = 5 + (2.5f * crand());
		p->length = 1;
		p->lengthVel = 0;
		p->rotation = rand() % 360;
	}
}

/*
 ==================
 CL_BulletParticles

 TODO: remove particle2 later, add a radius parameter for shotgun?
 ==================
*/
void CL_BulletParticles (const vec3_t org, const vec3_t dir, particleSource_t *particle, particleSource_t *particle2)
{
	particle_t	 *p;
	int			 i, count;

	count = 3 + (rand() % 5);

	// If underwater, do a bubble trail
	if (CL_PointContents(org, -1) & MASK_WATER)
	{
		CL_BubbleParticles(org, count, 0, cl.media.bubbleTrailParticle);
		return;
	}

	// Sparks
	for (i = 0; i < count; i++)
	{
		p = CL_AllocParticle();
		if (!p)
			return;

		p->material = particle->stages->material;
		p->time = cl.time;
		p->flags = PF_BOUNCE;

		p->org[0] = org[0] + dir[0] * 2 + crand();
		p->org[1] = org[1] + dir[1] * 2 + crand();
		p->org[2] = org[2] + dir[2] * 2 + crand();
		p->vel[0] = dir[0] * 180 + crand() * 60;
		p->vel[1] = dir[1] * 180 + crand() * 60;
		p->vel[2] = dir[2] * 180 + crand() * 60;
		p->accel[0] = 0;
		p->accel[1] = 0;
		p->accel[2] = -120 + (60 * crand());
		p->color[0] = particle->stages->color[0];
		p->color[1] = particle->stages->color[1];
		p->color[2] = particle->stages->color[2];
		p->fadeColor[0] = particle->stages->fadeColor[0];
		p->fadeColor[1] = particle->stages->fadeColor[1];
		p->fadeColor[2] = particle->stages->fadeColor[2];
		p->alpha = 1.0f;
		p->fadeAlpha = -8.0f;
		p->radius = 0.4f + (0.2f * crand());
		p->fadeRadius = 0;
		p->length = 8 + (4 * crand());
		p->lengthVel = 8 + (4 * crand());
		p->rotation = 0;
		p->bounceFactor = 0.2f;

		VectorCopy(p->org, p->lastOrg);
	}

	// Smoke
	for (i = 0; i < 3; i++)
	{
		p = CL_AllocParticle();
		if (!p)
			return;

		p->material = particle2->stages->material;
		p->time = cl.time;
		p->flags = PF_NONE;

		p->org[0] = org[0] + dir[0] * 5 + crand();
		p->org[1] = org[1] + dir[1] * 5 + crand();
		p->org[2] = org[2] + dir[2] * 5 + crand();
		p->vel[0] = crand() * 2.5f;
		p->vel[1] = crand() * 2.5f;
		p->vel[2] = crand() * 2.5f + (25 + crand() * 5);
		p->accel[0] = 0;
		p->accel[1] = 0;
		p->accel[2] = 0;
		p->color[0] = particle2->stages->color[0];
		p->color[1] = particle2->stages->color[1];
		p->color[2] = particle2->stages->color[2];
		p->fadeColor[0] = particle2->stages->fadeColor[0];
		p->fadeColor[1] = particle2->stages->fadeColor[1];
		p->fadeColor[2] = particle2->stages->fadeColor[2];
		p->alpha = 0.5f;
		p->fadeAlpha = -(0.4f + frand() * 0.2f);
		p->radius = 3 + (1.5f * crand());
		p->fadeRadius = 5 + (2.5f * crand());
		p->length = 1;
		p->lengthVel = 0;
		p->rotation = rand() % 360;
	}
}

void CL_ExplosionParticles (const vec3_t org)
{
}

void CL_BFGExplosionParticles (const vec3_t org)
{
}

/*
 ==================
 CL_SmokePuffParticles

 TODO: This could be used for more monsters
 ==================
*/
void CL_SmokePuffParticles (const vec3_t org, float radius, int count, particleSource_t *particle)
{
	particle_t	 *p;
	int			 i;

	for (i = 0; i < count; i++)
	{
		p = CL_AllocParticle();
		if (!p)
			return;

		p->material = particle->stages->material;
		p->time = cl.time;
		p->flags = PF_NONE;

		p->org[0] = org[0] + crand();
		p->org[1] = org[1] + crand();
		p->org[2] = org[2] + crand();
		p->vel[0] = crand() * 5;
		p->vel[1] = crand() * 5;
		p->vel[2] = crand() * 5 + (25 + crand() * 5);
		p->accel[0] = 0;
		p->accel[1] = 0;
		p->accel[2] = 0;
		p->color[0] = particle->stages->color[0];
		p->color[1] = particle->stages->color[1];
		p->color[2] = particle->stages->color[2];
		p->fadeColor[0] = particle->stages->fadeColor[0];
		p->fadeColor[1] = particle->stages->fadeColor[1];
		p->fadeColor[2] = particle->stages->fadeColor[2];
		p->alpha = 0.5f;
		p->fadeAlpha = -(0.4f + frand() * 0.2f);
		p->radius = radius + ((radius * 0.5f) * crand());
		p->fadeRadius = (radius * 2) + ((radius * 4) * crand());
		p->length = 1;
		p->lengthVel = 0;
		p->rotation = rand() % 360;
	}
}

/*
 ==================
 CL_BubbleParticles
 ==================
*/
void CL_BubbleParticles (const vec3_t org, int count, float magnitude, particleSource_t *particle)
{
	particle_t	*p;
	int			i;

	for (i = 0; i < count; i++)
	{
		p = CL_AllocParticle();
		if (!p)
			return;

		p->material = particle->stages->material;
		p->time = cl.time;
		p->flags = PF_UNDERWATER;

		p->org[0] = org[0] + (magnitude * crand());
		p->org[1] = org[1] + (magnitude * crand());
		p->org[2] = org[2] + (magnitude * crand());
		p->vel[0] = crand() * 5;
		p->vel[1] = crand() * 5;
		p->vel[2] = crand() * 5 + (25 + crand() * 5);
		p->accel[0] = 0;
		p->accel[1] = 0;
		p->accel[2] = 0;
		p->color[0] = particle->stages->color[0];
		p->color[1] = particle->stages->color[1];
		p->color[2] = particle->stages->color[2];
		p->fadeColor[0] = particle->stages->fadeColor[0];
		p->fadeColor[1] = particle->stages->fadeColor[1];
		p->fadeColor[2] = particle->stages->fadeColor[2];
		p->alpha = 1.0;
		p->fadeAlpha = -(0.4f + frand() * 0.2f);
		p->radius = 1 + (0.5f * crand());
		p->fadeRadius = 0;
		p->length = 1;
		p->lengthVel = 0;
		p->rotation = 0;
	}
}


/*
 ==============================================================================

    LIGHT STYLE MANAGEMENT

 ==============================================================================
*/

struct clightStyle_t {
	int		length;
	float	map[MAX_PATH_LENGTH];
	vec3_t	rgb;
};

static clightStyle_t	cl_lightStyles[MAX_LIGHTSTYLES];
static int				cl_lastOfs;


/*
 ==================
 CL_ClearLightStyles
 ==================
*/
void CL_ClearLightStyles ()
{
	memset(cl_lightStyles, 0, sizeof(cl_lightStyles));

	cl_lastOfs = -1;
}

/*
 ==================
 CL_RunLightStyles
 ==================
*/
void CL_RunLightStyles ()
{
	clightStyle_t	*ls;
	int				i, ofs;

	ofs = cl.time / 100;
	if (ofs == cl_lastOfs)
		return;
	cl_lastOfs = ofs;

	for (i = 0, ls = cl_lightStyles; i < MAX_LIGHTSTYLES; i++, ls++)
	{
		if (!ls->length)
		{
			ls->rgb[0] = ls->rgb[1] = ls->rgb[2] = 1.0;
			continue;
		}

		if (ls->length == 1)
			ls->rgb[0] = ls->rgb[1] = ls->rgb[2] = ls->map[0];
		else
			ls->rgb[0] = ls->rgb[1] = ls->rgb[2] = ls->map[ofs % ls->length];
	}
}

/*
 ==================
 CL_AddLightStyles
 ==================
*/
void CL_AddLightStyles ()
{
	clightStyle_t	*ls;
	int				i;

	for (i = 0, ls = cl_lightStyles; i < MAX_LIGHTSTYLES; i++, ls++)
		R_SetLightStyle(i, ls->rgb[0], ls->rgb[1], ls->rgb[2]);
}

/*
 ==================
 CL_SetLightStyle
 ==================
*/
void CL_SetLightStyle (int style)
{
	char	*s;
	int		i, length;

	if (style < 0 || style >= MAX_LIGHTSTYLES)
		Com_Error(ERR_DROP, "CL_SetLightStyle: style = %i", style);

	s = cl.configStrings[CS_LIGHTS + style];

	length = Str_Length(s);
	if (length >= MAX_PATH_LENGTH)
		Com_Error(ERR_DROP, "CL_SetLightStyle: style length = %i", length);

	cl_lightStyles[style].length = length;

	for (i = 0; i < length; i++)
		cl_lightStyles[style].map[i] = (float)(s[i] - 'a') / (float)('m' - 'a');
}


/*
 ==============================================================================

    DYNAMIC LIGHT MANAGEMENT

 ==============================================================================
*/

#define MAX_DLIGHTS			32

struct dlight_t {
	bool		active;
	int			start;
	int			end;
	vec3_t		origin;
	float		intensity;
	vec3_t		color;
	bool		fade;
};

static dlight_t	cl_dynamicLights[MAX_DLIGHTS];


/*
 ==================
 CL_AllocDynamicLight
 ==================
*/
static dlight_t *CL_AllocDynamicLight ()
{
	int		i;
	int		time, index;

	for (i = 0; i < MAX_DLIGHTS; i++)
	{
		if (!cl_dynamicLights[i].active)
		{
			memset(&cl_dynamicLights[i], 0, sizeof(dlight_t));
			return &cl_dynamicLights[i];
		}
	}

	// Find the oldest light
	time = cl.time;
	index = 0;

	for (i = 0; i < MAX_DLIGHTS; i++)
	{
		if (cl_dynamicLights[i].start < time)
		{
			time = cl_dynamicLights[i].start;
			index = i;
		}
	}

	memset(&cl_dynamicLights[index], 0, sizeof(dlight_t));
	return &cl_dynamicLights[index];
}

/*
 ==================
 CL_AddDynamicLights
 ==================
*/
void CL_AddDynamicLights ()
{
	int				i;
	dlight_t		*dl;
	renderLight_t   light;
	float			intensity;

	for (i = 0, dl = cl_dynamicLights; i < MAX_DLIGHTS; i++, dl++)
	{
		if (!dl->active)
			continue;

		if (cl.time >= dl->end)
		{
			dl->active = false;
			continue;
		}

		if (!dl->fade)
			intensity = dl->intensity;
		else
		{
			intensity = (float)(cl.time - dl->start) / (dl->end - dl->start);
			intensity = dl->intensity * (1.0f - intensity);
		}

		// Create a new light
		VectorCopy(dl->origin, light.origin);
		VectorClear(light.center);
		AxisClear(light.axis);
		VectorSet(light.radius, intensity, intensity, intensity);

		light.style = 0;
		light.detailLevel = 10;
		light.parallel = false;
		light.noShadows = false;

		light.customMaterial = NULL;
		light.materialParms[MATERIALPARM_RED] = dl->color[0];
		light.materialParms[MATERIALPARM_GREEN] = dl->color[1];
		light.materialParms[MATERIALPARM_BLUE] = dl->color[2];
		light.materialParms[MATERIALPARM_ALPHA] = 1.0f;
		light.materialParms[MATERIALPARM_TIME_OFFSET] = MS2SEC(dl->start);
		light.materialParms[MATERIALPARM_DIVERSITY] = 0.0f;
		light.materialParms[MATERIALPARM_GENERAL] = 0.0f;
		light.materialParms[MATERIALPARM_MODE] = 1.0f;

		// Send it to the renderer
		R_AddLightToScene(&light);
	}
}

/*
 ==================
 CL_ClearDynamicLights
 ==================
*/
void CL_ClearDynamicLights ()
{
	memset(cl_dynamicLights, 0, sizeof(cl_dynamicLights));
}

/*
 ==================
 CL_DynamicLight

 TODO: Add a material parameter for shell lights?
 ==================
*/
void CL_DynamicLight (const vec3_t org, float intensity, float r, float g, float b, bool fade, int duration)
{
	dlight_t		*dl;
	renderLight_t	light;

	// A duration of 0 means a temporary light, so we should just send
	// it to the renderer and forget about it
	if (duration == 0)
	{
		VectorCopy(org, light.origin);
		VectorClear(light.center);
		AxisClear(light.axis);
		VectorSet(light.radius, intensity, intensity, intensity);

		light.style = 0;
		light.detailLevel = 10;
		light.parallel = false;
		light.noShadows = false;

		light.customMaterial = NULL;
		light.materialParms[MATERIALPARM_RED] = r;
		light.materialParms[MATERIALPARM_GREEN] = g;
		light.materialParms[MATERIALPARM_BLUE] = b;
		light.materialParms[MATERIALPARM_ALPHA] = 1.0f;
		light.materialParms[MATERIALPARM_TIME_OFFSET] = 0.0f;
		light.materialParms[MATERIALPARM_DIVERSITY] = 0.0f;
		light.materialParms[MATERIALPARM_GENERAL] = 0.0f;
		light.materialParms[MATERIALPARM_MODE] = 1.0f;

		// Send it to the renderer
		R_AddLightToScene(&light);
		return;
	}

	// Add a new dynamic light
	dl = CL_AllocDynamicLight();
	dl->active = true;

	dl->start = cl.time;
	dl->end = dl->start + duration;
	VectorCopy(org, dl->origin);
	dl->intensity = intensity;
	dl->color[0] = r;
	dl->color[1] = g;
	dl->color[2] = b;
	dl->fade = fade;
}


/*
 ==============================================================================

    MUZZLE FLASH PARSING

 ==============================================================================
*/


/*
 ==================
 CL_ParsePlayerMuzzleFlash
 ==================
*/
void CL_ParsePlayerMuzzleFlash (sizebuf_t *msg)
{
	centity_t   *cent;
	vec3_t		forward, right;
	vec3_t	    origin;
	char		name[MAX_PATH_LENGTH];
	float		volume;
	int		    ent;
	int			weapon;

	ent = MSG_ReadShort(msg);
	if (ent < 1 || ent >= MAX_EDICTS)
		Com_Error(ERR_DROP, "CL_ParsePlayerMuzzleFlash: bad entity index");

	weapon = MSG_ReadByte(&net_message);
	if (weapon == -1)
		Com_Error(ERR_DROP, "CL_ParsePlayerMuzzleFlash: read past end of message");

	// Lower the volume if it has a silencer
	if (weapon & MZ_SILENCED)
	{
		weapon &= ~MZ_SILENCED;
		volume = 0.2f;
	}
	else
		volume = 1.0f;

	cent = &cl.entities[ent];

	cent->flashStartTime = MS2SEC(cl.time);
	cent->flashRotation = rand() % 360;

	// Set up flash angles and position
	AngleVectors(cent->current.angles, forward, right, NULL);

	origin[0] = cent->current.origin[0] + forward[0] * 18 + right[0] * 16;
	origin[1] = cent->current.origin[1] + forward[1] * 18 + right[1] * 16;
	origin[2] = cent->current.origin[2] + forward[2] * 18 + right[2] * 16;

	// Send the flash to the appropriate handler
	switch (weapon){
	case MZ_BLASTER:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/blastf1a.wav"), volume, ATTN_NORM, 0.0f);

		break;
	case MZ_BLUEHYPERBLASTER:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 0.0f, 0.0f, 1.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/hyprbf1a.wav"), volume, ATTN_NORM, 0.0f);

		break;
	case MZ_HYPERBLASTER:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/hyprbf1a.wav"), volume, ATTN_NORM, 0.0f);

		break;
	case MZ_MACHINEGUN:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		Str_SPrintf(name, sizeof(name), "weapons/machgf%ib.wav", (rand() % 5) + 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound(name), volume, ATTN_NORM, 0.0f);

		break;
	case MZ_SHOTGUN:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/shotgf1b.wav"), volume, ATTN_NORM, 0.0f);
		S_StartSound(NULL, ent, CHAN_AUTO, S_RegisterSound("weapons/shotgr1b.wav"), volume, ATTN_NORM, 0.1f);

		break;
	case MZ_SSHOTGUN:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/sshotf1b.wav"), volume, ATTN_NORM, 0.0f);

		break;
	case MZ_CHAINGUN1:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 0.25f, 0.0f, false, 1);
		Str_SPrintf(name, sizeof(name), "weapons/machgf%ib.wav", (rand() % 5) + 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound(name), volume, ATTN_NORM, 0.0f);

		break;
	case MZ_CHAINGUN2:
		CL_DynamicLight(origin, 255.0f + (rand()&31), 1.0f, 0.5f, 0.0f, false, 1);
		Str_SPrintf(name, sizeof(name), "weapons/machgf%ib.wav", (rand() % 5) + 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound(name), volume, ATTN_NORM, 0.0f);
		Str_SPrintf(name, sizeof(name), "weapons/machgf%ib.wav", (rand() % 5) + 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound(name), volume, ATTN_NORM, 0.5f);

		break;
	case MZ_CHAINGUN3:
		CL_DynamicLight(origin, 250.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		Str_SPrintf(name, sizeof(name), "weapons/machgf%ib.wav", (rand() % 5) + 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound(name), volume, ATTN_NORM, 0.0f);
		Str_SPrintf(name, sizeof(name), "weapons/machgf%ib.wav", (rand() % 5) + 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound(name), volume, ATTN_NORM, 0.033f);
		Str_SPrintf(name, sizeof(name), "weapons/machgf%ib.wav", (rand() % 5) + 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound(name), volume, ATTN_NORM, 0.066f);

		break;
	case MZ_RAILGUN:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 0.5f, 0.5f, 1.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/railgf1a.wav"), volume, ATTN_NORM, 0.0f);

		break;
	case MZ_ROCKET:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 0.5f, 0.2f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/rocklf1a.wav"), volume, ATTN_NORM, 0.0f);
		S_StartSound(NULL, ent, CHAN_AUTO, S_RegisterSound("weapons/rocklr1b.wav"), volume, ATTN_NORM, 0.1f);

		break;
	case MZ_GRENADE:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 0.5f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/grenlf1a.wav"), volume, ATTN_NORM, 0.0f);
		S_StartSound(NULL, ent, CHAN_AUTO, S_RegisterSound("weapons/grenlr1b.wav"), volume, ATTN_NORM, 0.1f);

		break;
	case MZ_BFG:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 0.0f, 1.0f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/bfg__f1y.wav"), volume, ATTN_NORM, 0.0f);

		break;
	case MZ_LOGIN:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 0.0f, 1.0f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ_LOGOUT:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 0.0f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ_RESPAWN:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ_PHALANX:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 0.5f, 0.5f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/plasshot.wav"), volume, ATTN_NORM, 0.0f);

		break;
	case MZ_IONRIPPER:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 0.5f, 0.5f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/rippfire.wav"), volume, ATTN_NORM, 0.0f);

		break;
	case MZ_ETF_RIFLE:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 0.9f, 0.7f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/nail1.wav"), volume, ATTN_NORM, 0.0f);

		break;
	case MZ_HEATBEAM:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 100.0f);

		break;
	case MZ_BLASTER2:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 0.0f, 1.0f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/blastf1a.wav"), volume, ATTN_NORM, 0.0f);

		break;
	case MZ_TRACKER:
		CL_DynamicLight(origin, 200.0f + (rand()&31), -1.0f, -1.0f, -1.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/disint2.wav"), volume, ATTN_NORM, 0.0f);

		break;		
	case MZ_NUKE1:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 0.0f, 0.0f, false, 100.0f);

		break;
	case MZ_NUKE2:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 100.0f);

		break;
	case MZ_NUKE4:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 0.0f, 0.0f, 1.0f, false, 100.0f);

		break;
	case MZ_NUKE8:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 0.0f, 1.0f, 1.0f, false, 100.0f);

		break;
	}
}

/*
 ==================
 CL_ParseMonsterMuzzleFlash
 ==================
*/
void CL_ParseMonsterMuzzleFlash (sizebuf_t *msg)
{
	centity_t   *cent;
	vec3_t		forward, right;
	vec3_t	    origin;
	char		name[MAX_PATH_LENGTH];
	int		    ent;
	int		    flash;

	ent = MSG_ReadShort(msg);
	if (ent < 1 || ent >= MAX_EDICTS)
		Com_Error(ERR_DROP, "CL_ParseMonsterMuzzleFlash: bad entity index");

	flash = MSG_ReadByte(msg);
	if (flash == -1)
		Com_Error(ERR_DROP, "CL_ParseMonsterMuzzleFlash: read past end of message");

	cent = &cl.entities[ent];

	cent->flashStartTime = MS2SEC(cl.time);
	cent->flashRotation = rand() % 360;

	// Set up flash angles and position
	AngleVectors(cent->current.angles, forward, right, NULL);
	
	origin[0] = cent->current.origin[0] + forward[0] * monster_flash_offset[flash][0] + right[0] * monster_flash_offset[flash][1];
	origin[1] = cent->current.origin[1] + forward[1] * monster_flash_offset[flash][0] + right[1] * monster_flash_offset[flash][1];
	origin[2] = cent->current.origin[2] + forward[2] * monster_flash_offset[flash][0] + right[2] * monster_flash_offset[flash][1] + monster_flash_offset[flash][2];

	// Send the flash to the appropriate handler
	switch (flash){
	case MZ2_INFANTRY_MACHINEGUN_1:
	case MZ2_INFANTRY_MACHINEGUN_2:
	case MZ2_INFANTRY_MACHINEGUN_3:
	case MZ2_INFANTRY_MACHINEGUN_4:
	case MZ2_INFANTRY_MACHINEGUN_5:
	case MZ2_INFANTRY_MACHINEGUN_6:
	case MZ2_INFANTRY_MACHINEGUN_7:
	case MZ2_INFANTRY_MACHINEGUN_8:
	case MZ2_INFANTRY_MACHINEGUN_9:
	case MZ2_INFANTRY_MACHINEGUN_10:
	case MZ2_INFANTRY_MACHINEGUN_11:
	case MZ2_INFANTRY_MACHINEGUN_12:
	case MZ2_INFANTRY_MACHINEGUN_13:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		CL_SmokePuffParticles(origin, 3, 1, cl.media.smokePuffParticle);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("infantry/infatck1.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_SOLDIER_MACHINEGUN_1:
	case MZ2_SOLDIER_MACHINEGUN_2:
	case MZ2_SOLDIER_MACHINEGUN_3:
	case MZ2_SOLDIER_MACHINEGUN_4:
	case MZ2_SOLDIER_MACHINEGUN_5:
	case MZ2_SOLDIER_MACHINEGUN_6:
	case MZ2_SOLDIER_MACHINEGUN_7:
	case MZ2_SOLDIER_MACHINEGUN_8:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		CL_SmokePuffParticles(origin, 3, 1, cl.media.smokePuffParticle);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("soldier/solatck3.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_GUNNER_MACHINEGUN_1:
	case MZ2_GUNNER_MACHINEGUN_2:
	case MZ2_GUNNER_MACHINEGUN_3:
	case MZ2_GUNNER_MACHINEGUN_4:
	case MZ2_GUNNER_MACHINEGUN_5:
	case MZ2_GUNNER_MACHINEGUN_6:
	case MZ2_GUNNER_MACHINEGUN_7:
	case MZ2_GUNNER_MACHINEGUN_8:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		CL_SmokePuffParticles(origin, 3, 1, cl.media.smokePuffParticle);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("gunner/gunatck2.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_ACTOR_MACHINEGUN_1:
	case MZ2_SUPERTANK_MACHINEGUN_1:
	case MZ2_SUPERTANK_MACHINEGUN_2:
	case MZ2_SUPERTANK_MACHINEGUN_3:
	case MZ2_SUPERTANK_MACHINEGUN_4:
	case MZ2_SUPERTANK_MACHINEGUN_5:
	case MZ2_SUPERTANK_MACHINEGUN_6:
	case MZ2_TURRET_MACHINEGUN:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		CL_SmokePuffParticles(origin, 3, 1, cl.media.smokePuffParticle);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("infantry/infatck1.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_BOSS2_MACHINEGUN_L1:
	case MZ2_BOSS2_MACHINEGUN_L2:
	case MZ2_BOSS2_MACHINEGUN_L3:
	case MZ2_BOSS2_MACHINEGUN_L4:
	case MZ2_BOSS2_MACHINEGUN_L5:
	case MZ2_CARRIER_MACHINEGUN_L1:
	case MZ2_CARRIER_MACHINEGUN_L2:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		CL_SmokePuffParticles(origin, 3, 1, cl.media.smokePuffParticle);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("infantry/infatck1.wav"), 1, ATTN_NONE, 0.0f);

		break;
	case MZ2_SOLDIER_BLASTER_1:
	case MZ2_SOLDIER_BLASTER_2:
	case MZ2_SOLDIER_BLASTER_3:
	case MZ2_SOLDIER_BLASTER_4:
	case MZ2_SOLDIER_BLASTER_5:
	case MZ2_SOLDIER_BLASTER_6:
	case MZ2_SOLDIER_BLASTER_7:
	case MZ2_SOLDIER_BLASTER_8:
	case MZ2_TURRET_BLASTER:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("soldier/solatck2.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_FLYER_BLASTER_1:
	case MZ2_FLYER_BLASTER_2:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("flyer/flyatck3.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_MEDIC_BLASTER_1:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("medic/medatck1.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_HOVER_BLASTER_1:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("hover/hovatck1.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_FLOAT_BLASTER_1:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("floater/fltatck1.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_SOLDIER_SHOTGUN_1:
	case MZ2_SOLDIER_SHOTGUN_2:
	case MZ2_SOLDIER_SHOTGUN_3:
	case MZ2_SOLDIER_SHOTGUN_4:
	case MZ2_SOLDIER_SHOTGUN_5:
	case MZ2_SOLDIER_SHOTGUN_6:
	case MZ2_SOLDIER_SHOTGUN_7:
	case MZ2_SOLDIER_SHOTGUN_8:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		CL_SmokePuffParticles(origin, 3, 1, cl.media.smokePuffParticle);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("soldier/solatck1.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_TANK_BLASTER_1:
	case MZ2_TANK_BLASTER_2:
	case MZ2_TANK_BLASTER_3:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/tnkatck3.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_TANK_MACHINEGUN_1:
	case MZ2_TANK_MACHINEGUN_2:
	case MZ2_TANK_MACHINEGUN_3:
	case MZ2_TANK_MACHINEGUN_4:
	case MZ2_TANK_MACHINEGUN_5:
	case MZ2_TANK_MACHINEGUN_6:
	case MZ2_TANK_MACHINEGUN_7:
	case MZ2_TANK_MACHINEGUN_8:
	case MZ2_TANK_MACHINEGUN_9:
	case MZ2_TANK_MACHINEGUN_10:
	case MZ2_TANK_MACHINEGUN_11:
	case MZ2_TANK_MACHINEGUN_12:
	case MZ2_TANK_MACHINEGUN_13:
	case MZ2_TANK_MACHINEGUN_14:
	case MZ2_TANK_MACHINEGUN_15:
	case MZ2_TANK_MACHINEGUN_16:
	case MZ2_TANK_MACHINEGUN_17:
	case MZ2_TANK_MACHINEGUN_18:
	case MZ2_TANK_MACHINEGUN_19:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		CL_SmokePuffParticles(origin, 3, 1, cl.media.smokePuffParticle);
		Str_SPrintf(name, sizeof(name), "tank/tnkatk2%c.wav", 'a' + rand() % 5);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound(name), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_CHICK_ROCKET_1:
	case MZ2_TURRET_ROCKET:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 0.5f, 0.2f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("chick/chkatck2.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_TANK_ROCKET_1:
	case MZ2_TANK_ROCKET_2:
	case MZ2_TANK_ROCKET_3:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 0.5f, 0.2f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/tnkatck1.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_SUPERTANK_ROCKET_1:
	case MZ2_SUPERTANK_ROCKET_2:
	case MZ2_SUPERTANK_ROCKET_3:
	case MZ2_BOSS2_ROCKET_1:
	case MZ2_BOSS2_ROCKET_2:
	case MZ2_BOSS2_ROCKET_3:
	case MZ2_BOSS2_ROCKET_4:
	case MZ2_CARRIER_ROCKET_1:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 0.5f, 0.2f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/rocket.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_GUNNER_GRENADE_1:
	case MZ2_GUNNER_GRENADE_2:
	case MZ2_GUNNER_GRENADE_3:
	case MZ2_GUNNER_GRENADE_4:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 0.5f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("gunner/gunatck3.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_GLADIATOR_RAILGUN_1:
	case MZ2_CARRIER_RAILGUN:
	case MZ2_WIDOW_RAIL:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 0.09f, 0.32f, 0.43f, false, 1);

		break;
	case MZ2_MAKRON_BFG:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 0.5f, 1.0f, 0.5f, false, 1);

		break;
	case MZ2_MAKRON_BLASTER_1:
	case MZ2_MAKRON_BLASTER_2:
	case MZ2_MAKRON_BLASTER_3:
	case MZ2_MAKRON_BLASTER_4:
	case MZ2_MAKRON_BLASTER_5:
	case MZ2_MAKRON_BLASTER_6:
	case MZ2_MAKRON_BLASTER_7:
	case MZ2_MAKRON_BLASTER_8:
	case MZ2_MAKRON_BLASTER_9:
	case MZ2_MAKRON_BLASTER_10:
	case MZ2_MAKRON_BLASTER_11:
	case MZ2_MAKRON_BLASTER_12:
	case MZ2_MAKRON_BLASTER_13:
	case MZ2_MAKRON_BLASTER_14:
	case MZ2_MAKRON_BLASTER_15:
	case MZ2_MAKRON_BLASTER_16:
	case MZ2_MAKRON_BLASTER_17:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("makron/blaster.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_JORG_MACHINEGUN_L1:
	case MZ2_JORG_MACHINEGUN_L2:
	case MZ2_JORG_MACHINEGUN_L3:
	case MZ2_JORG_MACHINEGUN_L4:
	case MZ2_JORG_MACHINEGUN_L5:
	case MZ2_JORG_MACHINEGUN_L6:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		CL_SmokePuffParticles(origin, 3, 1, cl.media.smokePuffParticle);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("boss3/xfire.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_JORG_MACHINEGUN_R1:
	case MZ2_JORG_MACHINEGUN_R2:
	case MZ2_JORG_MACHINEGUN_R3:
	case MZ2_JORG_MACHINEGUN_R4:
	case MZ2_JORG_MACHINEGUN_R5:
	case MZ2_JORG_MACHINEGUN_R6:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		CL_SmokePuffParticles(origin, 3, 1, cl.media.smokePuffParticle);

		break;
	case MZ2_JORG_BFG_1:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 0.5f, 1.0f, 0.5f, false, 1);

		break;
	case MZ2_BOSS2_MACHINEGUN_R1:
	case MZ2_BOSS2_MACHINEGUN_R2:
	case MZ2_BOSS2_MACHINEGUN_R3:
	case MZ2_BOSS2_MACHINEGUN_R4:
	case MZ2_BOSS2_MACHINEGUN_R5:
	case MZ2_CARRIER_MACHINEGUN_R1:
	case MZ2_CARRIER_MACHINEGUN_R2:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 1.0f, 1.0f, 0.0f, false, 1);
		CL_SmokePuffParticles(origin, 3, 1, cl.media.smokePuffParticle);

		break;
	case MZ2_STALKER_BLASTER:
	case MZ2_DAEDALUS_BLASTER:
	case MZ2_MEDIC_BLASTER_2:
	case MZ2_WIDOW_BLASTER:
	case MZ2_WIDOW_BLASTER_SWEEP1:
	case MZ2_WIDOW_BLASTER_SWEEP2:
	case MZ2_WIDOW_BLASTER_SWEEP3:
	case MZ2_WIDOW_BLASTER_SWEEP4:
	case MZ2_WIDOW_BLASTER_SWEEP5:
	case MZ2_WIDOW_BLASTER_SWEEP6:
	case MZ2_WIDOW_BLASTER_SWEEP7:
	case MZ2_WIDOW_BLASTER_SWEEP8:
	case MZ2_WIDOW_BLASTER_SWEEP9:
	case MZ2_WIDOW_BLASTER_100:
	case MZ2_WIDOW_BLASTER_90:
	case MZ2_WIDOW_BLASTER_80:
	case MZ2_WIDOW_BLASTER_70:
	case MZ2_WIDOW_BLASTER_60:
	case MZ2_WIDOW_BLASTER_50:
	case MZ2_WIDOW_BLASTER_40:
	case MZ2_WIDOW_BLASTER_30:
	case MZ2_WIDOW_BLASTER_20:
	case MZ2_WIDOW_BLASTER_10:
	case MZ2_WIDOW_BLASTER_0:
	case MZ2_WIDOW_BLASTER_10L:
	case MZ2_WIDOW_BLASTER_20L:
	case MZ2_WIDOW_BLASTER_30L:
	case MZ2_WIDOW_BLASTER_40L:
	case MZ2_WIDOW_BLASTER_50L:
	case MZ2_WIDOW_BLASTER_60L:
	case MZ2_WIDOW_BLASTER_70L:
	case MZ2_WIDOW_RUN_1:
	case MZ2_WIDOW_RUN_2:
	case MZ2_WIDOW_RUN_3:
	case MZ2_WIDOW_RUN_4:
	case MZ2_WIDOW_RUN_5:
	case MZ2_WIDOW_RUN_6:
	case MZ2_WIDOW_RUN_7:
	case MZ2_WIDOW_RUN_8:
		CL_DynamicLight(origin, 200.0f + (rand()&31), 0.0f, 1.0f, 0.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/tnkatck3.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_WIDOW_DISRUPTOR:
		CL_DynamicLight(origin, 200.0f + (rand()&31), -1.0f, -1.0f, -1.0f, false, 1);
		S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/disint2.wav"), 1, ATTN_NORM, 0.0f);

		break;
	case MZ2_WIDOW_PLASMABEAM:
	case MZ2_WIDOW2_BEAMER_1:
	case MZ2_WIDOW2_BEAMER_2:
	case MZ2_WIDOW2_BEAMER_3:
	case MZ2_WIDOW2_BEAMER_4:
	case MZ2_WIDOW2_BEAMER_5:
	case MZ2_WIDOW2_BEAM_SWEEP_1:
	case MZ2_WIDOW2_BEAM_SWEEP_2:
	case MZ2_WIDOW2_BEAM_SWEEP_3:
	case MZ2_WIDOW2_BEAM_SWEEP_4:
	case MZ2_WIDOW2_BEAM_SWEEP_5:
	case MZ2_WIDOW2_BEAM_SWEEP_6:
	case MZ2_WIDOW2_BEAM_SWEEP_7:
	case MZ2_WIDOW2_BEAM_SWEEP_8:
	case MZ2_WIDOW2_BEAM_SWEEP_9:
	case MZ2_WIDOW2_BEAM_SWEEP_10:
	case MZ2_WIDOW2_BEAM_SWEEP_11:
		CL_DynamicLight(origin, 300.0f + (rand()&100), 1.0f, 1.0f, 0.0f, false, 200);

		break;
	}
}